package controllers;

import java.util.HashSet;

import java.util.List;
import java.util.Set;

import models.Action;
import models.Component;
import models.Event;
import models.Project;
import models.Role;
import models.Task;
import models.User;
import models.UserRoleProject;
import models.UserStory;

public class Security extends Secure.Security {

	/**
	 * @param username
	 * @param password
	 * @return This method checks if the user was successfully connected
	 */
	public static boolean authentify(String username, String password) {
		User user = User.connect(username, password);
		if (user != null && !user.isDeactivated) {
			session.put("userId", user.getEntityId());
			session.put("userName", user.userName);
			return true;
		}
		return false;
	}

	/**
	 * This method renders the first page on logout
	 */
	static void onDisconnected() {
		render("Application/firstPage.html");
	}

	/**
	 * Checks if this role can do this action or not
	 * 
	 * @param actionName
	 *            the name of the action
	 * @param project
	 *            the project I'll be working on
	 */

	public static boolean checkPermission(String actionName, Project project) {
		long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);
		Action action = Action.find("byName", actionName).first();
		List<Role> roles = Role.find(
				"select urp.role from UserRoleProject urp where urp.user=?",
				user).fetch();
		for (int i = 0; i < roles.size(); i++)
			if (roles.get(i).isSystem
					&& roles.get(i).title.equals("System Admin"))
				return true;
		if (project != null)
			roles = UserRoleProject
					.find(
							"select r.role from UserRoleProject r where r.user = ? and r.project = ?",
							user, project).fetch();
		else if (project == null)
			roles = UserRoleProject
					.find(
							"select r.role from UserRoleProject r where r.user = ? and r.project = null",
							user).fetch();
		for (Role role : roles)
			if (role.actions.contains(action))
				return true;
		return false;
	}

	/**
	 * Checks whether the user has the action specified by
	 * <code>actionName</code> on the object of class <code>className</code> and
	 * generates a forbidden response in case he doesn't have the permission to
	 * do this action. It is called in the CONTROLLERS only
	 * 
	 * @param actionName
	 *            the name of the action
	 * @param id
	 *            the id of the object that the action will be performed on
	 * @param className
	 *            the class name of the object that the action will be performed
	 *            on
	 */
	static void checkAccess(String actionName, long id, String className) {
		boolean hasAccess = false;
		Project project = null;

		if (className.equals(Project.class.getName()))
			project = Project.findById(id);
		else if (className.equals(Event.class.getName())) {
			Event event = Event.findById(id);
			project = event.project;
		} else if (className.equals(Component.class.getName())) {
			Component comp = Component.findById(id);
			project = comp.project;
		} else if (className.equals(Task.class.getName())) {
			Task task = Task.findById(id);
			project = task.events.get(0).project;
		} else if (className.equals(UserStory.class.getName())) {
			UserStory story = UserStory.findById(id);
			project = story.component.project;
		}
		/*
		 * Each one should check that the code for the type he needs already
		 * exists, if not, then you should write the code for it, use the above
		 * code to help you, or if you don't get it post your question on the
		 * group
		 */

		hasAccess = checkPermission(actionName, project);

		// If he he has no access, generate a forbidden response
		if (!hasAccess)
			forbidden();
	}

	/**
	 * Checks whether the user has the permission to perform the action
	 * specified by <code>actionName</code> on the object that is inspected from
	 * the rendered objects in the view It is called in the VIEW only
	 * 
	 * @param actionName
	 *            the name of the action
	 * @return whether the user has this action or not
	 */
	static boolean check(String actionName) {

		Project project = null;

		if (renderArgs.get("project") != null)
			project = (Project) renderArgs.get("project");
		else if (renderArgs.get("sprint") != null) {
			Event sprint = (Event) renderArgs.get("sprint");
			project = sprint.project;
		} else if (renderArgs.get("event") != null) {
			Event event = (Event) renderArgs.get("event");
			project = event.project;
		} else if (renderArgs.get("component") != null) {
			Component comp = (Component) renderArgs.get("component");
			project = comp.project;
		} else if (renderArgs.get("task") != null) {
			Task task = (Task) renderArgs.get("task");
			project = task.events.get(0).project;
		} else if (renderArgs.get("userStory") != null) {
			UserStory userStory = (UserStory) renderArgs.get("userStory");
			project = userStory.component.project;
		}

		/*
		 * Each one should check that the code for the type he needs already
		 * exists, if not, then you should write the code for it, use the above
		 * code to help you, or if you don't get it post your question on the
		 * group
		 */
		return checkPermission(actionName, project);
	}

	/**
	 * Generates forbidden response if the parameters are not in the same
	 * project
	 * 
	 * @author Loai_Ghoraba
	 * @param params
	 *            the parameters sent, they may any mix of any order of model
	 *            objects Called to check against non_consistient Projects hack,
	 * 
	 * */
	static void checkInSameProject(Object... params) {
		/*
		 * If at any this list contains more than one project, return false;
		 * Since this is a set, adding the same project to it won't have any
		 * effect since set discard duplicates, so if we have two projects this
		 * means they are different
		 */
		Set<Project> projects = new HashSet<Project>();
		for (Object param : params) {
			System.out.println(params);
			// If two different projects are in the list, then return false
			if (param instanceof Project)
				projects.add((Project) param);
			else if (param instanceof Event) {
				Event event = (Event) param;
				projects.add(event.project);
			} else if (param instanceof Component) {
				Component comp = (Component) param;
				projects.add(comp.project);
			} else if (param instanceof Task) {
				Task task = (Task) param;
				projects.add(task.getProject());
			} else if (param instanceof UserStory) {
				UserStory story = (UserStory) param;
				projects.add(story.component.project);
			} else if (param instanceof Task[]) {
				Task[] tasks = (Task[]) param;
				for (Task task : tasks)
					projects.add(task.getProject());
			} else if (param instanceof UserStory[]) {
				UserStory[] stories = (UserStory[]) param;
				for (UserStory story : stories)
					projects.add(story.component.project);
			}
			if (projects.size() > 1)
				forbidden();
		}
	}
}